home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / bash_114.zip / bash-1.14.2 / builtins / common.c < prev    next >
C/C++ Source or Header  |  1994-06-26  |  19KB  |  828 lines

  1. /* Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
  2.  
  3.    This file is part of GNU Bash, the Bourne Again SHell.
  4.  
  5.    Bash is free software; you can redistribute it and/or modify it under
  6.    the terms of the GNU General Public License as published by the Free
  7.    Software Foundation; either version 1, or (at your option) any later
  8.    version.
  9.  
  10.    Bash is distributed in the hope that it will be useful, but WITHOUT ANY
  11.    WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12.    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13.    for more details.
  14.    
  15.    You should have received a copy of the GNU General Public License along
  16.    with Bash; see the file COPYING.  If not, write to the Free Software
  17.    Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  18.  
  19. #include <stdio.h>
  20. #include <sys/types.h>
  21. #include "../posixstat.h"
  22. #if defined (HAVE_VFPRINTF)
  23. #include <varargs.h>
  24. #endif /* VFPRINTF */
  25.  
  26. #if defined (HAVE_STRING_H)
  27. #  include <string.h>
  28. #else /* !HAVE_STRING_H */
  29. #  include <strings.h>
  30. #endif /* !HAVE_STRING_H */
  31.  
  32. #include "../shell.h"
  33. #include "../unwind_prot.h"
  34. #include "../maxpath.h"
  35. #include "../jobs.h"
  36. #include "../builtins.h"
  37. #include "../input.h"
  38. #include "../execute_cmd.h"
  39. #include "hashcom.h"
  40. #include "common.h"
  41. #include <tilde/tilde.h>
  42.  
  43. #if defined (HISTORY)
  44. #  include "../bashhist.h"
  45. #endif
  46.  
  47. extern int no_symbolic_links, interactive, interactive_shell;
  48. extern int indirection_level, startup_state;
  49. extern int hashing_disabled;
  50. extern int variable_context;
  51. extern char *this_command_name, *shell_name;
  52. extern COMMAND *global_command;
  53. extern HASH_TABLE *hashed_filenames;
  54.  
  55. /* Read a numeric arg for this_command_name, the name of the shell builtin
  56.    that wants it.  LIST is the word list that the arg is to come from. */
  57. int
  58. get_numeric_arg (list)
  59.      WORD_LIST *list;
  60. {
  61.   int count = 1;
  62.  
  63.   if (list)
  64.     {
  65.       register char *arg;
  66.       int sign = 1;
  67.  
  68.       arg = list->word->word;
  69.       if (!arg)
  70.     goto bad_number;
  71.  
  72.       /* Skip optional leading white space. */
  73.       while (whitespace (*arg))
  74.     arg++;
  75.  
  76.       if (!*arg)
  77.         goto bad_number;
  78.  
  79.       /* We allow leading `-' or `+'. */
  80.       if (*arg == '-' || *arg == '+')
  81.     {
  82.       if (!digit (arg[1]))
  83.         goto bad_number;
  84.  
  85.       if (*arg == '-')
  86.         sign = -1;
  87.  
  88.       arg++;
  89.     }
  90.  
  91.       for (count = 0; digit (*arg); arg++)
  92.     count = (count * 10) + digit_value (*arg);
  93.  
  94.       /* Skip trailing whitespace, if any. */
  95.       while (whitespace (*arg))
  96.         arg++;
  97.  
  98.       if (!*arg)
  99.         count = count * sign;
  100.       else
  101.     {
  102.   bad_number:
  103.       builtin_error ("bad non-numeric arg `%s'", list->word->word);
  104.       throw_to_top_level ();
  105.     }
  106.       no_args (list->next);
  107.     }
  108.   return (count);
  109. }
  110.  
  111. /* This is a lot like report_error (), but it is for shell builtins
  112.    instead of shell control structures, and it won't ever exit the
  113.    shell. */
  114. #if defined (HAVE_VFPRINTF)
  115. void
  116. builtin_error (va_alist)
  117.      va_dcl
  118. {
  119.   char *format;
  120.   va_list args;
  121.  
  122.   if (this_command_name && *this_command_name)
  123.     fprintf (stderr, "%s: ", this_command_name);
  124.  
  125.   va_start (args);
  126.   format = va_arg (args, char *);
  127.   vfprintf (stderr, format, args);
  128.   va_end (args);
  129.   fprintf (stderr, "\n");
  130. }
  131. #else /* !HAVE_VFPRINTF */
  132. void
  133. builtin_error (format, arg1, arg2, arg3, arg4, arg5)
  134.      char *format, *arg1, *arg2, *arg3, *arg4, *arg5;
  135. {
  136.   if (this_command_name && *this_command_name)
  137.     fprintf (stderr, "%s: ", this_command_name);
  138.  
  139.   fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
  140.   fprintf (stderr, "\n");
  141.   fflush (stderr);
  142. }
  143. #endif /* !HAVE_VFPRINTF */
  144.  
  145. /* Remember LIST in $0 ... $9, and REST_OF_ARGS.  If DESTRUCTIVE is
  146.    non-zero, then discard whatever the existing arguments are, else
  147.    only discard the ones that are to be replaced. */
  148. void
  149. remember_args (list, destructive)
  150.      WORD_LIST *list;
  151.      int destructive;
  152. {
  153.   register int i;
  154.  
  155.   for (i = 1; i < 10; i++)
  156.     {
  157.       if (destructive && dollar_vars[i])
  158.     {
  159.       free (dollar_vars[i]);
  160.       dollar_vars[i] = (char *)NULL;
  161.     }
  162.  
  163.       if (list)
  164.     {
  165.       if (!destructive && dollar_vars[i])
  166.         free (dollar_vars[i]);
  167.  
  168.       dollar_vars[i] = savestring (list->word->word);
  169.       list = list->next;
  170.     }
  171.     }
  172.  
  173.   /* If arguments remain, assign them to REST_OF_ARGS.
  174.      Note that copy_word_list (NULL) returns NULL, and
  175.      that dispose_words (NULL) does nothing. */
  176.   if (destructive || list)
  177.     {
  178.       dispose_words (rest_of_args);
  179.       rest_of_args = copy_word_list (list);
  180.     }
  181.  
  182.   if (destructive)
  183.     set_dollar_vars_changed ();
  184. }
  185.  
  186. /* Return if LIST is NULL else barf and jump to top_level. */
  187. void
  188. no_args (list)
  189.      WORD_LIST *list;
  190. {
  191.   if (list)
  192.     {
  193.       builtin_error ("extra arguments");
  194.       longjmp (top_level, DISCARD);
  195.     }
  196. }
  197.  
  198. /* Return the octal number parsed from STRING, or -1 to indicate
  199.    that the string contained a bad number. */
  200. int
  201. read_octal (string)
  202.      char *string;
  203. {
  204.   int result = 0;
  205.   int digits = 0;
  206.  
  207.   while (*string && *string >= '0' && *string < '8')
  208.     {
  209.       digits++;
  210.       result = (result * 8) + *string++ - '0';
  211.     }
  212.  
  213.   if (!digits || result > 0777 || *string)
  214.     result = -1;
  215.  
  216.   return (result);
  217. }
  218.  
  219. /* Temporary static. */
  220. static char *dotted_filename = (char *)NULL;
  221.  
  222. /* Return the full pathname that FILENAME hashes to.  If FILENAME
  223.    is hashed, but data->check_dot is non-zero, check ./FILENAME
  224.    and return that if it is executable. */
  225. char *
  226. find_hashed_filename (filename)
  227.      char *filename;
  228. {
  229.   register BUCKET_CONTENTS *item;
  230.  
  231.   if (hashing_disabled)
  232.     return ((char *)NULL);
  233.  
  234.   item = find_hash_item (filename, hashed_filenames);
  235.  
  236.   if (item)
  237.     {
  238.       /* If this filename is hashed, but `.' comes before it in the path,
  239.      then see if `./filename' is an executable. */
  240.       if (pathdata(item)->check_dot)
  241.     {
  242.       if (dotted_filename)
  243.         free (dotted_filename);
  244.  
  245.       dotted_filename = (char *)xmalloc (3 + strlen (filename));
  246.       strcpy (dotted_filename, "./");
  247.       strcat (dotted_filename, filename);
  248.  
  249.       if (executable_file (dotted_filename))
  250.         return (dotted_filename);
  251.  
  252.       /* Watch out.  If this file was hashed to "./filename", and
  253.          "./filename" is not executable, then return NULL. */
  254.  
  255.       /* Since we already know "./filename" is not executable, what
  256.          we're really interested in is whether or not the `path'
  257.          portion of the hashed filename is equivalent to the current
  258.          directory, but only if it starts with a `.'.  (This catches
  259.          ./. and so on.)  same_file () is in execute_cmd.c; it tests
  260.          general Unix file equivalence -- same device and inode. */
  261.       {
  262.         char *path = pathdata (item)->path;
  263.  
  264.         if (*path == '.')
  265.           {
  266.         int same = 0;
  267.         char *tail;
  268.  
  269.         tail = (char *) strrchr (path, '/');
  270.  
  271.         if (tail)
  272.           {
  273.             *tail = '\0';
  274.             same = same_file
  275.               (".", path, (struct stat *)NULL, (struct stat *)NULL);
  276.             *tail = '/';
  277.           }
  278.         if (same)
  279.           return ((char *)NULL);
  280.           }
  281.       }
  282.     }
  283.       return (pathdata (item)->path);
  284.     }
  285.   else
  286.     return ((char *)NULL);
  287. }
  288.  
  289. /* Remove FILENAME from the table of hashed commands. */
  290. void
  291. remove_hashed_filename (filename)
  292.      char *filename;
  293. {
  294.   register BUCKET_CONTENTS *item;
  295.  
  296.   if (hashing_disabled)
  297.     return;
  298.  
  299.   item = remove_hash_item (filename, hashed_filenames);
  300.   if (item)
  301.     {
  302.       if (item->data)
  303.         {
  304.       free (pathdata(item)->path);
  305.       free (item->data);
  306.         }
  307.       if (item->key)
  308.     free (item->key);
  309.       free (item);
  310.     }
  311. }
  312.  
  313. /* **************************************************************** */
  314. /*                                    */
  315. /*            Pushing and Popping a Context            */
  316. /*                                    */
  317. /* **************************************************************** */
  318.  
  319. static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
  320. static int dollar_arg_stack_slots = 0;
  321. static int dollar_arg_stack_index = 0;
  322.  
  323. void
  324. push_context ()
  325. {
  326.   push_dollar_vars ();
  327.   variable_context++;
  328. }
  329.  
  330. void
  331. pop_context ()
  332. {
  333.   pop_dollar_vars ();
  334.   kill_all_local_variables ();
  335.   variable_context--;
  336. }
  337.  
  338. /* Save the existing positional parameters on a stack. */
  339. void
  340. push_dollar_vars ()
  341. {
  342.   if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
  343.     {
  344.       dollar_arg_stack = (WORD_LIST **)
  345.     xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
  346.           * sizeof (WORD_LIST **));
  347.     }
  348.   dollar_arg_stack[dollar_arg_stack_index] = list_rest_of_args ();
  349.   dollar_arg_stack[++dollar_arg_stack_index] = (WORD_LIST *)NULL;
  350. }
  351.  
  352. /* Restore the positional parameters from our stack. */
  353. void
  354. pop_dollar_vars ()
  355. {
  356.   if (!dollar_arg_stack || !dollar_arg_stack_index)
  357.     return;
  358.  
  359.   remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
  360.   dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
  361.   dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
  362. }
  363.  
  364. void
  365. dispose_saved_dollar_vars ()
  366. {
  367.   if (!dollar_arg_stack || !dollar_arg_stack_index)
  368.     return;
  369.  
  370.   dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
  371.   dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
  372. }
  373.  
  374. static int changed_dollar_vars = 0;
  375.  
  376. /* Have the dollar variables been reset to new values since we last
  377.    checked? */
  378. dollar_vars_changed ()
  379. {
  380.   return (changed_dollar_vars);
  381. }
  382.  
  383. void
  384. set_dollar_vars_unchanged ()
  385. {
  386.   changed_dollar_vars = 0;
  387. }
  388.  
  389. void
  390. set_dollar_vars_changed ()
  391. {
  392.   changed_dollar_vars  = 1;
  393. }
  394.  
  395. /* Function called when one of the builtin commands detects a bad
  396.    option. */
  397. void
  398. bad_option (s)
  399.      char *s;
  400. {
  401.   builtin_error ("unknown option: %s", s);
  402. }
  403.  
  404. /* Return a consed string which is the current working directory.
  405.    FOR_WHOM is the name of the caller for error printing.  */
  406. char *the_current_working_directory = (char *)NULL;
  407.  
  408. char *
  409. get_working_directory (for_whom)
  410.      char *for_whom;
  411. {
  412.   if (no_symbolic_links)
  413.     {
  414.       if (the_current_working_directory)
  415.     free (the_current_working_directory);
  416.  
  417.       the_current_working_directory = (char *)NULL;
  418.     }
  419.  
  420.   if (!the_current_working_directory)
  421.     {
  422.       char *directory;
  423.  
  424.       the_current_working_directory = xmalloc (MAXPATHLEN);
  425.       directory = getwd (the_current_working_directory);
  426.       if (!directory)
  427.     {
  428.       if (for_whom && *for_whom)
  429.         fprintf (stderr, "%s: ", for_whom);
  430.       else
  431.         fprintf (stderr, "%s: ", get_name_for_error ());
  432.  
  433.       fprintf (stderr, "could not get current directory: %s\n\r",
  434.            the_current_working_directory);
  435.  
  436.       free (the_current_working_directory);
  437.       the_current_working_directory = (char *)NULL;
  438.       return (char *)NULL;
  439.     }
  440.     }
  441.  
  442.   return (savestring (the_current_working_directory));
  443. }
  444.  
  445. /* Make NAME our internal idea of the current working directory. */
  446. void
  447. set_working_directory (name)
  448.      char *name;
  449. {
  450.   if (the_current_working_directory)
  451.     free (the_current_working_directory);
  452.  
  453.   the_current_working_directory = savestring (name);
  454. }
  455.  
  456. #if defined (JOB_CONTROL)
  457. /* Return the job spec found in LIST. */
  458. get_job_spec (list)
  459.      WORD_LIST *list;
  460. {
  461.   register char *word;
  462.   int job = NO_JOB;
  463.   int substring = 0;
  464.  
  465.   if (!list)
  466.     return (current_job);
  467.  
  468.   word = list->word->word;
  469.  
  470.   if (!*word)
  471.     return (current_job);
  472.  
  473.   if (*word == '%')
  474.     word++;
  475.  
  476.   if (digit (*word) && (sscanf (word, "%d", &job) == 1))
  477.     return (job - 1);
  478.  
  479.   switch (*word)
  480.     {
  481.     case 0:
  482.     case '%':
  483.     case '+':
  484.       return (current_job);
  485.  
  486.     case '-':
  487.       return (previous_job);
  488.  
  489.     case '?':            /* Substring search requested. */
  490.       substring++;
  491.       word++;
  492.       goto find_string;
  493.  
  494.     default:
  495.     find_string:
  496.       {
  497.     register int i, wl = strlen (word);
  498.     for (i = 0; i < job_slots; i++)
  499.       {
  500.         if (jobs[i])
  501.           {
  502.         register PROCESS *p = jobs[i]->pipe;
  503.         do
  504.           {
  505.             if ((substring && strindex (p->command, word)) ||
  506.             (strncmp (p->command, word, wl) == 0))
  507.               if (job != NO_JOB)
  508.             {
  509.               builtin_error ("ambigious job spec: %s", word);
  510.               return (DUP_JOB);
  511.             }
  512.               else
  513.             job = i;
  514.  
  515.             p = p->next;
  516.           }
  517.         while (p != jobs[i]->pipe);
  518.           }
  519.       }
  520.     return (job);
  521.       }
  522.     }
  523. }
  524. #endif /* JOB_CONTROL */
  525.  
  526. int parse_and_execute_level = 0;
  527.  
  528. /* How to force parse_and_execute () to clean up after itself. */
  529. void
  530. parse_and_execute_cleanup ()
  531. {
  532.   run_unwind_frame ("parse_and_execute_top");
  533. }
  534.  
  535. /* Parse and execute the commands in STRING.  Returns whatever
  536.    execute_command () returns.  This frees STRING.  INTERACT is
  537.    the new value for `interactive' while the commands are being
  538.    executed.  A value of -1 means don't change it. */
  539. int
  540. parse_and_execute (string, from_file, interact)
  541.      char *string;
  542.      char *from_file;
  543.      int interact;
  544. {
  545.   int last_result = EXECUTION_SUCCESS;
  546.   int code = 0, jump_to_top_level = 0;
  547.   char *orig_string = string;
  548.  
  549.   /* Unwind protect this invocation of parse_and_execute (). */
  550.   begin_unwind_frame ("parse_and_execute_top");
  551.   unwind_protect_int (parse_and_execute_level);
  552.   unwind_protect_jmp_buf (top_level);
  553.   unwind_protect_int (indirection_level);
  554.   if (interact != -1 && interactive != interact)
  555.     unwind_protect_int (interactive);
  556.  
  557. #if defined (HISTORY)
  558.   if (interactive_shell)
  559.     {
  560.       unwind_protect_int (remember_on_history);
  561. #  if defined (BANG_HISTORY)
  562.       unwind_protect_int (history_expansion_inhibited);
  563. #  endif /* BANG_HISTORY */
  564.     }
  565. #endif /* HISTORY */
  566.  
  567.   add_unwind_protect (pop_stream, (char *)NULL);
  568.   if (orig_string)
  569.     add_unwind_protect (xfree, orig_string);
  570.   end_unwind_frame ();
  571.  
  572.   parse_and_execute_level++;
  573.   push_stream ();
  574.   indirection_level++;
  575.   if (interact != -1)
  576.     interactive = interact;
  577.  
  578. #if defined (HISTORY)
  579.   /* We don't remember text read by the shell this way on
  580.      the history list, and we don't use !$ in shell scripts. */
  581.   remember_on_history = 0;
  582. #  if defined (BANG_HISTORY)
  583.   history_expansion_inhibited = 1;
  584. #  endif /* BANG_HISTORY */
  585. #endif /* HISTORY */
  586.  
  587.   with_input_from_string (string, from_file);
  588.   {
  589.     COMMAND *command;
  590.  
  591.     while (*(bash_input.location.string))
  592.       {
  593.     if (interrupt_state)
  594.       {
  595.         last_result = EXECUTION_FAILURE;
  596.         break;
  597.       }
  598.  
  599.     /* Provide a location for functions which `longjmp (top_level)' to
  600.        jump to.  This prevents errors in substitution from restarting
  601.        the reader loop directly, for example. */
  602.     code = setjmp (top_level);
  603.  
  604.     if (code)
  605.       {
  606.         jump_to_top_level = 0;
  607.         switch (code)
  608.           {
  609.           case FORCE_EOF:
  610.           case EXITPROG:
  611.         run_unwind_frame ("pe_dispose");
  612.         /* Remember to call longjmp (top_level) after the old
  613.            value for it is restored. */
  614.         jump_to_top_level = 1;
  615.         goto out;
  616.  
  617.           case DISCARD:
  618.         run_unwind_frame ("pe_dispose");
  619.         continue;
  620.  
  621.           default:
  622.         programming_error ("bad jump to top_level: %d", code);
  623.         break;
  624.           }
  625.       }
  626.       
  627.     if (parse_command () == 0)
  628.       {
  629.         if ((command = global_command) != (COMMAND *)NULL)
  630.           {
  631.         struct fd_bitmap *bitmap;
  632.  
  633.         bitmap = new_fd_bitmap (FD_BITMAP_SIZE);
  634.         begin_unwind_frame ("pe_dispose");
  635.         add_unwind_protect (dispose_fd_bitmap, bitmap);
  636.  
  637.         global_command = (COMMAND *)NULL;
  638.  
  639. #if defined (ONESHOT)
  640.             if (startup_state == 2 && *bash_input.location.string == '\0' &&
  641.             command->type == cm_simple && !command->redirects &&
  642.             !command->value.Simple->redirects)
  643.           {
  644.             command->flags |= CMD_NO_FORK;
  645.             command->value.Simple->flags |= CMD_NO_FORK;
  646.           }
  647. #endif /* ONESHOT */
  648.     
  649.         last_result = execute_command_internal
  650.             (command, 0, NO_PIPE, NO_PIPE, bitmap);
  651.  
  652.         dispose_command (command);
  653.         run_unwind_frame ("pe_dispose");
  654.           }
  655.       }
  656.     else
  657.       {
  658.         last_result = EXECUTION_FAILURE;
  659.  
  660.         /* Since we are shell compatible, syntax errors in a script
  661.            abort the execution of the script.  Right? */
  662.         break;
  663.       }
  664.       }
  665.   }
  666.  
  667.  out:
  668.  
  669.   run_unwind_frame ("parse_and_execute_top");
  670.  
  671.   if (interrupt_state && parse_and_execute_level == 0)
  672.     {
  673.       /* An interrupt during non-interactive execution in an
  674.          interactive shell (e.g. via $PROMPT_COMMAND) should
  675.          not cause the shell to exit. */
  676.       interactive = interactive_shell;
  677.       throw_to_top_level ();
  678.     }
  679.  
  680.   if (jump_to_top_level)
  681.     longjmp (top_level, code);
  682.  
  683.   return (last_result);
  684. }
  685.  
  686. /* Return the address of the builtin named NAME.
  687.    DISABLED_OKAY means find it even if the builtin is disabled. */
  688. static Function *
  689. builtin_address_internal (name, disabled_okay)
  690.      char *name;
  691.      int disabled_okay;
  692. {
  693.   int hi, lo, mid, j;
  694.  
  695.   hi = num_shell_builtins - 1;
  696.   lo = 0;
  697.  
  698.   while (lo <= hi)
  699.     {
  700.       mid = (lo + hi) / 2;
  701.  
  702.       j = shell_builtins[mid].name[0] - name[0];
  703.  
  704.       if (j == 0)
  705.     j = strcmp (shell_builtins[mid].name, name);
  706.  
  707.       if (j == 0)
  708.     {
  709.       /* It must have a function pointer.  It must be enabled, or we
  710.          must have explicitly allowed disabled functions to be found. */
  711.       if (shell_builtins[mid].function &&
  712.           ((shell_builtins[mid].flags & BUILTIN_ENABLED) || disabled_okay))
  713.         return (shell_builtins[mid].function);
  714.       else
  715.         return ((Function *)NULL);
  716.     }
  717.       if (j > 0)
  718.     hi = mid - 1;
  719.       else
  720.     lo = mid + 1;
  721.     }
  722.   return ((Function *)NULL);
  723. }
  724.  
  725. /* Perform a binary search and return the address of the builtin function
  726.    whose name is NAME.  If the function couldn't be found, or the builtin
  727.    is disabled or has no function associated with it, return NULL. */
  728. Function *
  729. find_shell_builtin (name)
  730.     char *name;
  731. {
  732.   return (builtin_address_internal (name, 0));
  733. }
  734.  
  735. /* Return the address of builtin with NAME, irregardless of its state of
  736.    enableness. */
  737. Function *
  738. builtin_address (name)
  739.      char *name;
  740. {
  741.   return (builtin_address_internal (name, 1));
  742. }
  743.  
  744. static int
  745. shell_builtin_compare (sbp1, sbp2)
  746.      struct builtin *sbp1, *sbp2;
  747. {
  748.   int result;
  749.  
  750.   if ((result = sbp1->name[0] - sbp2->name[0]) == 0)
  751.     result = strcmp (sbp1->name, sbp2->name);
  752.  
  753.   return (result);
  754. }
  755.  
  756. /* Sort the table of shell builtins so that the binary search will work
  757.    in find_shell_builtin. */
  758. void
  759. initialize_shell_builtins ()
  760. {
  761.   qsort (shell_builtins, num_shell_builtins, sizeof (struct builtin),
  762.     shell_builtin_compare);
  763. }
  764.  
  765. /* Return a new string which is the quoted version of STRING.  This is used
  766.    by alias and trap. */
  767. char *
  768. single_quote (string)
  769.      char *string;
  770. {
  771.   register int i, j, c;
  772.   char *result;
  773.  
  774.   result = (char *)xmalloc (3 + (3 * strlen (string)));
  775.  
  776.   result[0] = '\'';
  777.  
  778.   for (i = 0, j = 1; string && (c = string[i]); i++)
  779.     {
  780.       result[j++] = c;
  781.  
  782.       if (c == '\'')
  783.     {
  784.       result[j++] = '\\';    /* insert escaped single quote */
  785.       result[j++] = '\'';
  786.       result[j++] = '\'';    /* start new quoted string */
  787.     }
  788.     }
  789.  
  790.   result[j++] = '\'';
  791.   result[j] = '\0';
  792.  
  793.   return (result);
  794. }
  795.  
  796. char *
  797. double_quote (string)
  798.      char *string;
  799. {
  800.   register int i, j, c;
  801.   char *result;
  802.  
  803.   result = (char *)xmalloc (3 + (3 * strlen (string)));
  804.  
  805.   result[0] = '"';
  806.  
  807.   for (i = 0, j = 1; string && (c = string[i]); i++)
  808.     {
  809.       switch (c)
  810.         {
  811.     case '"':
  812.     case '$':
  813.     case '`':
  814.     case '\n':
  815.     case '\\':
  816.       result[j++] = '\\';
  817.     default:
  818.       result[j++] = c;
  819.       break;
  820.         }
  821.     }
  822.  
  823.   result[j++] = '"';
  824.   result[j] = '\0';
  825.  
  826.   return (result);
  827. }
  828.